// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <iterator>

#include <tbir/runtime/container/unicode.h>
#include "_item_type_traits.h"

namespace tbir::runtime::details {


    template<class Iterator, class ValueType>
    using enumerate_iterator_base =
    std::iterator<typename std::iterator_traits<Iterator>::iterator_category,
            std::pair<int64_t, ValueType>>;

    template<class Iterator, class ValueType, typename TRANSFORM_TYPE>
    struct enumerate_iterator : public enumerate_iterator_base<Iterator, ValueType> {
        int64_t value = 0;
        Iterator iter;

        enumerate_iterator() = default;

        enumerate_iterator(Iterator const &iter, int64_t first) : value(first), iter(iter) {
        }

        typename enumerate_iterator_base<Iterator, ValueType>::value_type operator*() const {
            return std::make_pair(value, transform_value(*iter, TRANSFORM_TYPE()));
        }

        enumerate_iterator &operator++() {
            ++value, ++iter;
            return *this;
        }

        enumerate_iterator &operator+=(int64_t n) {
            value += n, iter += n;
            return *this;
        }

        bool operator!=(enumerate_iterator const &other) const {
            return !(*this == other);
        }

        bool operator<(enumerate_iterator const &other) const {
            return iter < other.iter;
        }

        int64_t operator-(enumerate_iterator const &other) const {
            return iter - other.iter;
        }

        bool operator==(enumerate_iterator const &other) const {
            return iter == other.iter;
        }

    private:
        template<typename T_ELE>
        Unicode transform_value(T_ELE &&ele,
                                std::integral_constant<int, ITEM_TRANSFORM_TYPE_UNICODE>) const {
            return Unicode(1, ele);
        };

        template<typename T_ELE>
        T_ELE transform_value(T_ELE &&ele,
                              std::integral_constant<int, ITEM_TRANSFORM_TYPE_DEFAULT>) const {
            return ele;
        };
    };

    template<class Iterable>
    struct enumerate
            : private std::remove_cv<typename std::remove_reference<Iterable>::type>::type,
              public enumerate_iterator<
                      typename std::remove_cv<typename std::remove_reference<Iterable>::type>::type::iterator,
                      typename item_type_traits<
                              typename std::remove_cv<typename std::remove_reference<Iterable>::type>::type>::type,
                      ITEM_TRANSFORM_TYPE < Iterable>> {
        using iterator = enumerate_iterator<
                typename std::remove_cv<typename std::remove_reference<Iterable>::type>::type::iterator,
                typename item_type_traits<
                        typename std::remove_cv<typename std::remove_reference<Iterable>::type>::type>::type,
                ITEM_TRANSFORM_TYPE < Iterable>>;
        using iterator::operator*;
        iterator end_iter;

        enumerate() {
        }

        enumerate(Iterable seq, int64_t first)
        : Iterable (std::move(seq)),
         iterator(Iterable::begin(), first),
         end_iter(Iterable::end(), -1) {
        }

        iterator &begin() {
            return *this;
        }

        iterator const &begin() const {
            return *this;
        }

        iterator end() const {
            return end_iter;
        }
    };

}  // namespace tbir::runtime::details

namespace tbir::runtime {
    template<class Iterable>
    details::enumerate<typename std::remove_cv<typename std::remove_reference<Iterable>::type>::type>
    enumerate(Iterable &&seq, int64_t first = 0L) {
        return {std::forward<Iterable>(seq), first};
    }

}  // namespace tbir::runtime
